load packages

library(ggplot2)
library(data.table)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
data.table 1.14.2 using 1 threads (see ?getDTthreads).  Latest news: r-datatable.com
**********
This installation of data.table has not detected OpenMP support. It should still work but in single-threaded mode.
This is a Mac. Please read https://mac.r-project.org/openmp/. Please engage with Apple and ask them for support. Check r-datatable.com for updates, and our Mac instructions here: https://github.com/Rdatatable/data.table/wiki/Installation. After several years of many reports of installation problems on Mac, it's time to gingerly point out that there have been no similar problems on Windows or Linux.
**********
library(stringr)
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:data.table’:

    between, first, last

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyr)
library(rprojroot)
library(vegan)
Loading required package: permute
Loading required package: lattice
This is vegan 2.6-2
library(ggpubr)
library(rstatix)

Attaching package: ‘rstatix’

The following object is masked from ‘package:stats’:

    filter
library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:ggpubr’:

    get_legend
library(emmeans)

set paths and filenames

### files
long_table=sprintf("%s/data/mp142_TGVG1.1_MPA4_combined_abundance_table_longform1.tsv",
                   find_rstudio_root_file())
metadata_table=sprintf("%s/data/some_teddy_MP142_metadata2.all_samples1.delivery.csv",
                       find_rstudio_root_file())

IA_groups_file=sprintf("%s/data/MP142_CASE_CNTRL_IA_LIST1.csv", 
                        find_rstudio_root_file())

load long table and metadata, merge

long_dt <- fread(sprintf("%s", long_table), sep = "\t", header = T) %>%
  select(sampleID, rel_abundance, lineage) %>%
  mutate(sampleID = as.character(sampleID),
         kingdom = case_when(grepl("k__Bac", lineage) ~ "Bacteria", 
                             grepl("k__Vir", lineage) ~ "Virus",
                             grepl("k__Ar", lineage) ~ "Archea",
                             grepl("k__Euk", lineage) ~ "Eukaryota",
                             TRUE ~ "other"))

groups_dt <- fread(IA_groups_file, sep = ",", header = T) %>%
  select(c(mask_id, case_ind))

meta_dt <- fread(sprintf("%s", metadata_table), sep = ",", header = T) %>%
  select(-V1) %>%
  mutate(sample = as.character(sample))

merge_full_dt <- merge(long_dt, meta_dt, by.x = "sampleID", by.y ="sample")

## filtering down to only subject from TEDDY T1D groups

merge_dt <- merge(merge_full_dt, groups_dt, by = "mask_id")

rm(merge_full_dt)

wide format, bray-curtis, VIROME


mask_id_list <- as.list(
  merge_dt %>% distinct(mask_id)
)

big_bray_long <- data.table(sample_info1=character(), 
                            sample_info2=character(), 
                            value=numeric())

## big for loop to do each subject
for (subjectq in mask_id_list[1]$mask_id) {
  temp_long_dt <- merge_dt %>%
    filter(mask_id == subjectq)
  
  temp_long_dt$sample_info <- str_c(temp_long_dt$sampleID, "@", 
                                    temp_long_dt$mask_id, "@", 
                                    temp_long_dt$age_days)
  
  temp_long_dt <- temp_long_dt %>%
    select(c(sample_info, rel_abundance, lineage, kingdom)) %>%
    distinct()
  if(nrow(temp_long_dt) > 1){
    wide_virome_temp_dt <- temp_long_dt %>%
      filter(kingdom == "Virus") %>%
      select(-kingdom) %>%
        pivot_wider(names_from = lineage, 
                  values_from = rel_abundance, 
                  values_fill = 0)
    sample_info_l <- wide_virome_temp_dt$sample_info
  
    wide_virome_temp_dt <- wide_virome_temp_dt %>% select(-sample_info)
    
    bray_curtis_dist <- vegdist(wide_virome_temp_dt, method="bray")
    
    bray_curtis_mat <- as.matrix(bray_curtis_dist)
    
    bray_curtis_df <- as.data.frame(bray_curtis_mat)
    
    colnames(bray_curtis_df) <- sample_info_l
    rownames(bray_curtis_df) <- sample_info_l
    bray_curtis_df$sample_info1 <- rownames(bray_curtis_df)
  
    bray_long <- melt(setDT(bray_curtis_df), 
                      id.vars = c("sample_info1"), 
                      variable.name = "sample_info2") %>%
      filter(sample_info1 != sample_info2)
    big_bray_long <- rbind(big_bray_long, bray_long)
  }
}

big_bray_long <- big_bray_long[, 
                               c("sampleID1", "mask_id1", "day_of_life1") 
                               := tstrsplit(sample_info1, "@", fixed=TRUE)]

big_bray_long <- big_bray_long[, 
                               c("sampleID2", "mask_id2", "day_of_life2") 
                               := tstrsplit(sample_info2, "@", fixed=TRUE)] %>%
  select(c(sampleID1, sampleID2, mask_id1, day_of_life1, day_of_life2, value)) %>%
  mutate(days_apart = as.numeric(day_of_life2) - as.numeric(day_of_life1)) %>%
  filter(days_apart >= 1) 
  
big_bray_long <- big_bray_long %>%
  rename(bray_curtis = value)

quick plot of days apart vs bray-curtis distance, virome

merge(big_bray_long, meta_dt, 
      by.x = "sampleID1", by.y = "sample") %>%
  ggplot(aes(x= days_apart, y = bray_curtis, color = IA)) +
  geom_point(alpha = 0.005) +
  geom_smooth() +
  theme_bw()
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

NA
NA

get bray-curtis distance to next sample VIROME

vir_next_bray_dt <- big_bray_long %>%
  group_by(mask_id1) %>%
  filter(n_distinct(sampleID1) >= 2) %>%
  ungroup() %>%
  group_by(sampleID1) %>%
  filter(days_apart == min(days_apart))

vir_next_bray_dt <- merge(vir_next_bray_dt, meta_dt, 
                          by.x = "sampleID1", by.y = "sample")

## add groups
vir_next_bray_dt <- merge(vir_next_bray_dt, groups_dt, by = "mask_id")

IA_lm <- lm(days_apart ~ bray_curtis*IA, 
             data= vir_next_bray_dt %>%
               filter(days_apart <= 100,
                      days_apart >= 10))

( fitted.emt <- emtrends(IA_lm, "IA", var = "bray_curtis") )
 IA  bray_curtis.trend   SE   df lower.CL upper.CL
 No               16.4 1.29 9557     13.9     18.9
 Yes              12.2 1.05 9557     10.1     14.2

Confidence level used: 0.95 
pw_IA <- pairs(fitted.emt)

pw_IA
 contrast estimate   SE   df t.ratio p.value
 No - Yes     4.25 1.66 9557   2.554  0.0107
vir_days_p <- vir_next_bray_dt %>%
  filter(days_apart <= 100,
         days_apart >=10) %>%
  ggplot(aes(x= days_apart, y = bray_curtis, color = IA)) +
  geom_point(alpha = 0.1) +
  geom_smooth(
    method = "lm"
    ) +
  geom_text(y = 0.9,
            x = 50,
            label = "p-value = 0.0107",
            color = "black") +
  scale_color_manual(values = c("#798E87", "#C27D38")) +
  theme_bw()  +
  labs(x = "Days to Next Sample",
       y = "Bray-Curtis Dissimilarity")

vir_days_p
`geom_smooth()` using formula = 'y ~ x'
ggsave(
  vir_days_p,
  file = sprintf(
    "%s/charts/bray_curtis_next_vir_days_IA.pdf",
    find_rstudio_root_file()
  ),
  height = 5, width = 3.5
)
`geom_smooth()` using formula = 'y ~ x'

average bray distance by subject/pair VIROME

vir_bray_sum_subj <- vir_next_bray_dt %>%
  group_by(mask_id1) %>%
  summarize(average_bray = mean(bray_curtis),
            IA = IA,
            pair_num = case_ind) %>%
  ungroup() %>%
  distinct() 
`summarise()` has grouped output by 'mask_id1'. You can override using the `.groups` argument.
vir_bray_sum_subj %>%
  group_by(pair_num) %>%
  filter(n() != 2) %>%
  arrange(pair_num)


vir_bray_subj_wide <- vir_bray_sum_subj %>%
  group_by(pair_num) %>%
  # making sure there are 2 in each pair
  filter(n() == 2) %>%
  ungroup() %>%
  # making sure that one is yes and one is no (for IA)
  group_by(pair_num, IA) %>%
  filter(n() == 1) %>%
  ungroup() %>%
  pivot_wider(id_cols = pair_num, names_from = IA, values_from = average_bray)
vir_bray_subj_wide %>%
  ggpaired(cond1 = "Yes", cond2 = "No",
    fill = "condition", palette = c("#798E87", "#C27D38"),
    line.color = "lightgrey", ggtheme = theme_bw(), line.size = 0.1) +
  stat_compare_means(paired = TRUE) 

NA

paired wilcox test

wilcox.test(vir_bray_subj_wide$Yes, vir_bray_subj_wide$No, paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  vir_bray_subj_wide$Yes and vir_bray_subj_wide$No
V = 26356, p-value = 0.2734
alternative hypothesis: true location shift is not equal to 0

new plot average bray distance by T1D and country status VIROME


vir_bray_sum_subj  %>%
  group_by(pair_num) %>%
  filter(n() == 2) %>%
  ggplot(aes(IA, average_bray)) +
  geom_boxplot(aes(fill = IA), outlier.shape = NA, alpha = 0.6) +
  geom_point(color="grey20", alpha = 0.5, stroke = 0) +
  geom_line(aes(group = pair_num), color = "lightgrey", alpha = 0.3) +
  geom_text(label = "Wilcoxon,\np-value = 0.2734", x = "No", 
            y = 1, size = 8/.pt) +
  scale_fill_manual(values=c("#798E87", "#C27D38")) +
  ylim(c(0.2, 1.05)) +
  theme_bw() +
  theme(legend.position = "bottom", 
        axis.text.x = element_blank()) +
  labs(y = "Bray-Curtis Distance\nto Follow-up Sample",
       title="virus SGBs")
Warning: Removed 2 rows containing non-finite values (`stat_boxplot()`).
Warning: Removed 2 rows containing missing values (`geom_point()`).
Warning: Removed 2 rows containing missing values (`geom_line()`).
ggsave(file = sprintf("%s/charts/virome_bray_curtis_IA_NCC_paired.pdf",
                      find_rstudio_root_file()), 
                 width = 2.5, height = 3.5)  
Warning: Removed 2 rows containing non-finite values (`stat_boxplot()`).
Warning: Removed 2 rows containing missing values (`geom_point()`).
Warning: Removed 2 rows containing missing values (`geom_line()`).

vir_bray_sum_subj  %>%
  group_by(pair_num) %>%
  filter(n() == 2) %>%
  ungroup() %>%
  group_by(IA) %>%
  summarize(avg_bray_t1d = mean(average_bray),
            n = n_distinct(mask_id1))
## this functions rounds a number up to the nearest 100
round_any = function(x, accuracy, f=ceiling){f(x/ accuracy) * accuracy}

DOL_vir_pair_dt <- vir_next_bray_dt %>%
  mutate(rounded_DOL =  round_any(age_days, 100)) %>%
  filter(rounded_DOL <= 1400) %>%
  group_by(mask_id1, rounded_DOL) %>%
  summarize(average_bray = mean(bray_curtis),
            IA = IA,
            pair_num = case_ind) %>%
  ungroup() %>%
  distinct() 
`summarise()` has grouped output by 'mask_id1', 'rounded_DOL'. You can override using the `.groups`
argument.
DOL_vir_pair_subj_wide <- DOL_vir_pair_dt %>%
  group_by(pair_num, rounded_DOL) %>%
  # making sure there are 2 in each pair
  filter(n() == 2) %>%
  ungroup() %>%
  # making sure that one is yes and one is no (for IA)
  group_by(pair_num, IA, rounded_DOL) %>%
  filter(n() == 1) %>%
  ungroup() %>%
  pivot_wider(id_cols = c(pair_num, rounded_DOL), names_from = IA, 
              values_from = average_bray)
DOL_vir_pair_subj_wide %>%
  ggpaired(cond1 = "Yes", cond2 = "No",
    fill = "condition", 
    palette = c("#798E87", "#C27D38"),
    line.color = "lightgrey", ggtheme = theme_bw(), line.size = 0.1,
    facet.by = "rounded_DOL") +
  stat_compare_means(paired = TRUE) 

DOL_vir_pair_wilcox_dt <- DOL_vir_pair_subj_wide %>% 
  group_by(rounded_DOL) %>%
  do(w = wilcox.test(.$Yes, .$No, data=., paired=TRUE)) %>% 
       summarise(rounded_DOL, 
                 Wilcox = w$p.value,
                 aster = case_when(Wilcox < 0.0001 ~ "****",
                                   Wilcox < 0.001 ~ "***", 
                                   Wilcox < 0.01 ~ "**",
                                   Wilcox < 0.05 ~ "*",
                                   TRUE ~ "ns"))

DOL_vir_pair_wil_dt <- merge(DOL_vir_pair_dt, DOL_vir_pair_wilcox_dt, by = "rounded_DOL")
DOL_vir_pair_wil_dt %>%
#  group_by(pair_num) %>%
  # making sure there are 2 in each pair
#  filter(n() == 2) %>%
#  ungroup() %>%
  # making sure that one is yes and one is no (for IA)
#  group_by(pair_num, IA) %>%
#  filter(n() == 1) %>%
#  ungroup() %>%
  ggplot(aes(IA, average_bray)) +
  geom_boxplot(aes(fill = IA), outlier.shape = NA, alpha = 0.6) +
  geom_point(color="grey20", alpha = 0.5, stroke = 0, size = 0.8) +
  geom_line(aes(group = pair_num), color = "lightgrey", alpha = 0.3) +
  geom_text(data = DOL_vir_pair_wilcox_dt, aes(label = aster), x = "No", y = 1.05) +
  scale_fill_manual(values=c("#798E87", "#C27D38")) +
  ylim(c(NA, 1.1)) +
  facet_wrap(vars(rounded_DOL), nrow = 1) +
  theme_bw() +
  theme(legend.position = "bottom", 
        axis.text.x = element_blank()) +
  labs(x = "day of life (rounded)", 
       y = "Bray-Curtis Distance\nto Follow-up Sample",
       title="virus SGBs")

ggsave(file = sprintf("%s/charts/virome_IA_and_dayoflife_bray_curtis_NCC_paired.pdf",
                      find_rstudio_root_file()), 
                 width = 6, height = 3.5)  

intrasubject bray-curtis, BACTERIOME


mask_id_list <- as.list(
  merge_dt %>% distinct(mask_id)
)

bac_bray_long <- data.table(sample_info1=character(), 
                            sample_info2=character(), 
                            value=numeric())

for (subjectq in mask_id_list[1]$mask_id) {
  temp_long_dt <- merge_dt %>%
    filter(mask_id == subjectq)
  
  temp_long_dt$sample_info <- str_c(temp_long_dt$sampleID, "@", 
                                    temp_long_dt$mask_id, "@", 
                                    temp_long_dt$age_days)
  
  temp_long_dt <- temp_long_dt %>%
    select(c(sample_info, rel_abundance, lineage, kingdom)) %>%
    distinct()
  if(nrow(temp_long_dt) > 1){
    wide_virome_temp_dt <- temp_long_dt %>%
      filter(kingdom == "Bacteria") %>%
      select(-kingdom) %>%
        pivot_wider(names_from = lineage, 
                  values_from = rel_abundance, 
                  values_fill = 0)
    sample_info_l <- wide_virome_temp_dt$sample_info
  
    wide_virome_temp_dt <- wide_virome_temp_dt %>% select(-sample_info)
    
    bray_curtis_dist <- vegdist(wide_virome_temp_dt, method="bray")
    
    bray_curtis_mat <- as.matrix(bray_curtis_dist)
    
    bray_curtis_df <- as.data.frame(bray_curtis_mat)
    
    colnames(bray_curtis_df) <- sample_info_l
    rownames(bray_curtis_df) <- sample_info_l
    bray_curtis_df$sample_info1 <- rownames(bray_curtis_df)
  
    bray_long <- melt(setDT(bray_curtis_df), 
                      id.vars = c("sample_info1"), 
                      variable.name = "sample_info2") %>%
      filter(sample_info1 != sample_info2)
    bac_bray_long <- rbind(bac_bray_long, bray_long)
  }
}

bac_bray_long <- bac_bray_long[, 
                               c("sampleID1", "mask_id1", "day_of_life1") 
                               := tstrsplit(sample_info1, "@", fixed=TRUE)]

bac_bray_long <- bac_bray_long[, 
                               c("sampleID2", "mask_id2", "day_of_life2") 
                               := tstrsplit(sample_info2, "@", fixed=TRUE)] %>%
  select(c(sampleID1, sampleID2, mask_id1, day_of_life1, day_of_life2, value)) %>%
  mutate(days_apart = as.numeric(day_of_life2) - as.numeric(day_of_life1)) %>%
  filter(days_apart >= 1) 
  
bac_bray_long <- bac_bray_long %>%
  rename(bray_curtis = value)

quick plot of days apart vs bray-curtis distance, bacteriome

bac_bray_long %>%
  ggplot(aes(x= days_apart, y = bray_curtis)) +
  geom_point(alpha = 0.005, color = "cadetblue") +
  geom_smooth() +
  theme_bw()
`geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

get bray-curtis distance to next sample BACTERIOME

bac_next_bray_dt <- bac_bray_long %>%
  group_by(mask_id1) %>%
  filter(n_distinct(sampleID1) >= 2) %>%
  ungroup() %>%
  group_by(sampleID1) %>%
  filter(days_apart == min(days_apart))

bac_next_bray_dt <- merge(bac_next_bray_dt, meta_dt, by.x = "sampleID1", by.y = "sample")

## add groups
bac_next_bray_dt <- merge(bac_next_bray_dt, groups_dt, by = "mask_id")

bac_IA_lm <- lm(days_apart ~ bray_curtis*IA, 
             data= bac_next_bray_dt %>%
               filter(days_apart <= 100,
                      days_apart >= 10))

( bac_fitted.emt <- emtrends(bac_IA_lm, "IA", var = "bray_curtis") )
 IA  bray_curtis.trend   SE   df lower.CL upper.CL
 No               14.5 1.33 9558     11.9     17.1
 Yes              12.8 1.07 9558     10.7     14.9

Confidence level used: 0.95 
pw_bac_IA <- pairs(bac_fitted.emt)

pw_bac_IA
 contrast estimate  SE   df t.ratio p.value
 No - Yes      1.7 1.7 9558   1.001  0.3169
bac_days_p <- bac_next_bray_dt %>%
  filter(days_apart <= 100,
         days_apart >=10) %>%
  ggplot(aes(x= days_apart, y = bray_curtis, color = IA)) +
  geom_point(alpha = 0.1) +
  geom_smooth(
    method = "lm"
    ) +
  geom_text(y = 0.9, x = 50,
            label = "p-value = 0.3169",
            color = "black") +
  scale_color_manual(values = c("#798E87", "#C27D38")) +
  theme_bw() +
  labs(x = "Days to Next Sample",
       y = "Bray-Curtis Dissimilarity")

bac_days_p
`geom_smooth()` using formula = 'y ~ x'
ggsave(
  bac_days_p,
  file = sprintf(
    "%s/charts/bray_curtis_IA_next_bac_days1.pdf",
    find_rstudio_root_file()
  ),
  height = 5, width = 3.5
)
`geom_smooth()` using formula = 'y ~ x'

average bray distance by subject/pair BACTERIOME

bac_bray_sum_subj <- bac_next_bray_dt %>%
  group_by(mask_id1) %>%
  summarize(average_bray = mean(bray_curtis),
            IA = IA,
            pair_num = case_ind) %>%
  ungroup() %>%
  distinct() 
`summarise()` has grouped output by 'mask_id1'. You can override using the `.groups` argument.


bac_bray_subj_wide <- bac_bray_sum_subj %>%
  group_by(pair_num) %>%
  filter(n() == 2) %>%
  ungroup() %>%
  # making sure that one is yes and one is no (for IA)
  group_by(pair_num, IA) %>%
  filter(n() == 1) %>%
  ungroup() %>%
  pivot_wider(id_cols = pair_num, names_from = IA, values_from = average_bray)
bac_bray_subj_wide %>%
  ggpaired(cond1 = "Yes", cond2 = "No",
    fill = "condition", palette = c("#798E87", "#C27D38"),
    line.color = "lightgrey", ggtheme = theme_bw(), line.size = 0.1) +
  stat_compare_means(paired = TRUE) 

NA
wilcox.test(bac_bray_subj_wide$Yes, bac_bray_subj_wide$No, paired = TRUE)

    Wilcoxon signed rank test with continuity correction

data:  bac_bray_subj_wide$Yes and bac_bray_subj_wide$No
V = 26335, p-value = 0.2683
alternative hypothesis: true location shift is not equal to 0

new plot average bray distance by IA and country status bacterioome

ggsave(file = sprintf("%s/charts/bacteriome_bray_curtis_IA_NCC_paired.pdf",
                      find_rstudio_root_file()), 
                 width = 2.5, height = 3.5)  
Warning: Removed 4 rows containing non-finite values (`stat_boxplot()`).
Warning: Removed 4 rows containing missing values (`geom_point()`).
Warning: Removed 4 rows containing missing values (`geom_line()`).

DOL BACTERIOME

## this functions rounds a number up to the nearest 100
round_any = function(x, accuracy, f=ceiling){f(x/ accuracy) * accuracy}

DOL_bac_pair_dt <- bac_next_bray_dt %>%
  mutate(rounded_DOL =  round_any(age_days, 100)) %>%
  filter(rounded_DOL <= 1400) %>%
  group_by(mask_id1, rounded_DOL) %>%
  summarize(average_bray = mean(bray_curtis),
            IA = IA,
            pair_num = case_ind) %>%
  ungroup() %>%
  distinct() 
`summarise()` has grouped output by 'mask_id1', 'rounded_DOL'. You can override using the `.groups`
argument.
DOL_bac_pair_subj_wide <- DOL_bac_pair_dt %>%
  group_by(pair_num, rounded_DOL) %>%
  filter(n() == 2) %>%
  ungroup() %>%
  group_by(pair_num, rounded_DOL, IA) %>%
  filter(n() == 1) %>%
  ungroup() %>%
  pivot_wider(id_cols = c(pair_num, rounded_DOL), names_from = T1D, values_from = average_bray)
Error in `pivot_wider()`:
! Can't subset columns that don't exist.
✖ Column `T1D` doesn't exist.
Backtrace:
 1. ... %>% ...
 3. tidyr:::pivot_wider.data.frame(., id_cols = c(pair_num, rounded_DOL), names_from = T1D, values_from = average_bray)
DOL_bac_pair_subj_wide %>%
  ggpaired(cond1 = "Yes", cond2 = "No",
    fill = "condition", palette = c("#798E87", "#C27D38"),
    line.color = "lightgrey", ggtheme = theme_bw(), line.size = 0.1,
    facet.by = "rounded_DOL") +
  stat_compare_means(paired = TRUE) 

DOL_bac_pair_wilcox_dt <- DOL_bac_pair_subj_wide %>% 
  group_by(rounded_DOL) %>%
  do(w = wilcox.test(.$Yes, .$No, data=., paired=TRUE)) %>% 
       summarise(rounded_DOL, 
                 Wilcox = w$p.value,
                 aster = case_when(Wilcox < 0.0001 ~ "****",
                                   Wilcox < 0.001 ~ "***", 
                                   Wilcox < 0.01 ~ "**",
                                   Wilcox < 0.05 ~ "*",
                                   TRUE ~ "ns"))

DOL_bac_pair_wil_dt <- merge(DOL_bac_pair_dt, DOL_bac_pair_wilcox_dt, by = "rounded_DOL")
DOL_bac_pair_wil_dt %>%
  group_by(rounded_DOL,pair_num) %>%
  filter(n() == 2) %>%
  ungroup() %>%
  group_by(pair_num, rounded_DOL, IA) %>%
  filter(n() == 1) %>%
  ungroup() %>%
  ggplot(aes(IA, average_bray)) +
  geom_boxplot(aes(fill = IA), outlier.shape = NA, alpha = 0.6) +
  geom_point(color="grey20", alpha = 0.5, stroke = 0, size = 0.8) +
  geom_line(aes(group = pair_num), color = "lightgrey", alpha = 0.2) +
  geom_text(data = DOL_bac_pair_wilcox_dt, aes(label = aster), x = "No", y = 1.05) +
  scale_fill_manual(values=c("#798E87", "#C27D38")) +
  ylim(c(NA, 1.1)) +
  facet_wrap(vars(rounded_DOL), nrow = 1) +
  theme_bw() +
  theme(legend.position = "bottom", 
        axis.text.x = element_blank()) +
  labs(x = "day of life (rounded)", 
       y = "Bray-Curtis Distance\nto Follow-up Sample",
       title="bacteria SGBs")

ggsave(file = sprintf("%s/charts/bacteriome_IA_and_dayoflife_bray_curtis_NCC_paired.pdf",
                      find_rstudio_root_file()), 
                 width = 6, height = 3.5)  

LS0tCnRpdGxlOiAiQnJheS1DdXJ0aXMgc2FtcGxlLXRvLXNhbXBsZSBhbmQgU2hhbm5vbiBkaXZlcnNpdHksIElBIHN0YXR1cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCmxvYWQgcGFja2FnZXMKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHJwcm9qcm9vdCkKbGlicmFyeSh2ZWdhbikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkocnN0YXRpeCkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KGVtbWVhbnMpCmBgYAoKc2V0IHBhdGhzIGFuZCBmaWxlbmFtZXMKCmBgYHtyfQojIyMgZmlsZXMKbG9uZ190YWJsZT1zcHJpbnRmKCIlcy9kYXRhL21wMTQyX1RHVkcxLjFfTVBBNF9jb21iaW5lZF9hYnVuZGFuY2VfdGFibGVfbG9uZ2Zvcm0xLnRzdiIsCiAgICAgICAgICAgICAgICAgICBmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCm1ldGFkYXRhX3RhYmxlPXNwcmludGYoIiVzL2RhdGEvc29tZV90ZWRkeV9NUDE0Ml9tZXRhZGF0YTIuYWxsX3NhbXBsZXMxLmRlbGl2ZXJ5LmNzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpKQoKSUFfZ3JvdXBzX2ZpbGU9c3ByaW50ZigiJXMvZGF0YS9NUDE0Ml9DQVNFX0NOVFJMX0lBX0xJU1QxLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICBmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCgpgYGAKCmxvYWQgbG9uZyB0YWJsZSBhbmQgbWV0YWRhdGEsIG1lcmdlCgpgYGB7cn0KbG9uZ19kdCA8LSBmcmVhZChzcHJpbnRmKCIlcyIsIGxvbmdfdGFibGUpLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUKSAlPiUKICBzZWxlY3Qoc2FtcGxlSUQsIHJlbF9hYnVuZGFuY2UsIGxpbmVhZ2UpICU+JQogIG11dGF0ZShzYW1wbGVJRCA9IGFzLmNoYXJhY3RlcihzYW1wbGVJRCksCiAgICAgICAgIGtpbmdkb20gPSBjYXNlX3doZW4oZ3JlcGwoImtfX0JhYyIsIGxpbmVhZ2UpIH4gIkJhY3RlcmlhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoImtfX1ZpciIsIGxpbmVhZ2UpIH4gIlZpcnVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgia19fQXIiLCBsaW5lYWdlKSB+ICJBcmNoZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJrX19FdWsiLCBsaW5lYWdlKSB+ICJFdWthcnlvdGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAib3RoZXIiKSkKCmdyb3Vwc19kdCA8LSBmcmVhZChJQV9ncm91cHNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUKSAlPiUKICBzZWxlY3QoYyhtYXNrX2lkLCBjYXNlX2luZCkpCgptZXRhX2R0IDwtIGZyZWFkKHNwcmludGYoIiVzIiwgbWV0YWRhdGFfdGFibGUpLCBzZXAgPSAiLCIsIGhlYWRlciA9IFQpICU+JQogIHNlbGVjdCgtVjEpICU+JQogIG11dGF0ZShzYW1wbGUgPSBhcy5jaGFyYWN0ZXIoc2FtcGxlKSkKCm1lcmdlX2Z1bGxfZHQgPC0gbWVyZ2UobG9uZ19kdCwgbWV0YV9kdCwgYnkueCA9ICJzYW1wbGVJRCIsIGJ5LnkgPSJzYW1wbGUiKQoKIyMgZmlsdGVyaW5nIGRvd24gdG8gb25seSBzdWJqZWN0IGZyb20gVEVERFkgVDFEIGdyb3VwcwoKbWVyZ2VfZHQgPC0gbWVyZ2UobWVyZ2VfZnVsbF9kdCwgZ3JvdXBzX2R0LCBieSA9ICJtYXNrX2lkIikKCnJtKG1lcmdlX2Z1bGxfZHQpCmBgYAoKCgp3aWRlIGZvcm1hdCwgYnJheS1jdXJ0aXMsIFZJUk9NRQoKYGBge3J9CgptYXNrX2lkX2xpc3QgPC0gYXMubGlzdCgKICBtZXJnZV9kdCAlPiUgZGlzdGluY3QobWFza19pZCkKKQoKYmlnX2JyYXlfbG9uZyA8LSBkYXRhLnRhYmxlKHNhbXBsZV9pbmZvMT1jaGFyYWN0ZXIoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfaW5mbzI9Y2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9bnVtZXJpYygpKQoKIyMgYmlnIGZvciBsb29wIHRvIGRvIGVhY2ggc3ViamVjdApmb3IgKHN1YmplY3RxIGluIG1hc2tfaWRfbGlzdFsxXSRtYXNrX2lkKSB7CiAgdGVtcF9sb25nX2R0IDwtIG1lcmdlX2R0ICU+JQogICAgZmlsdGVyKG1hc2tfaWQgPT0gc3ViamVjdHEpCiAgCiAgdGVtcF9sb25nX2R0JHNhbXBsZV9pbmZvIDwtIHN0cl9jKHRlbXBfbG9uZ19kdCRzYW1wbGVJRCwgIkAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVtcF9sb25nX2R0JG1hc2tfaWQsICJAIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBfbG9uZ19kdCRhZ2VfZGF5cykKICAKICB0ZW1wX2xvbmdfZHQgPC0gdGVtcF9sb25nX2R0ICU+JQogICAgc2VsZWN0KGMoc2FtcGxlX2luZm8sIHJlbF9hYnVuZGFuY2UsIGxpbmVhZ2UsIGtpbmdkb20pKSAlPiUKICAgIGRpc3RpbmN0KCkKICBpZihucm93KHRlbXBfbG9uZ19kdCkgPiAxKXsKICAgIHdpZGVfdmlyb21lX3RlbXBfZHQgPC0gdGVtcF9sb25nX2R0ICU+JQogICAgICBmaWx0ZXIoa2luZ2RvbSA9PSAiVmlydXMiKSAlPiUKICAgICAgc2VsZWN0KC1raW5nZG9tKSAlPiUKICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbGluZWFnZSwgCiAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gcmVsX2FidW5kYW5jZSwgCiAgICAgICAgICAgICAgICAgIHZhbHVlc19maWxsID0gMCkKICAgIHNhbXBsZV9pbmZvX2wgPC0gd2lkZV92aXJvbWVfdGVtcF9kdCRzYW1wbGVfaW5mbwogIAogICAgd2lkZV92aXJvbWVfdGVtcF9kdCA8LSB3aWRlX3Zpcm9tZV90ZW1wX2R0ICU+JSBzZWxlY3QoLXNhbXBsZV9pbmZvKQogICAgCiAgICBicmF5X2N1cnRpc19kaXN0IDwtIHZlZ2Rpc3Qod2lkZV92aXJvbWVfdGVtcF9kdCwgbWV0aG9kPSJicmF5IikKICAgIAogICAgYnJheV9jdXJ0aXNfbWF0IDwtIGFzLm1hdHJpeChicmF5X2N1cnRpc19kaXN0KQogICAgCiAgICBicmF5X2N1cnRpc19kZiA8LSBhcy5kYXRhLmZyYW1lKGJyYXlfY3VydGlzX21hdCkKICAgIAogICAgY29sbmFtZXMoYnJheV9jdXJ0aXNfZGYpIDwtIHNhbXBsZV9pbmZvX2wKICAgIHJvd25hbWVzKGJyYXlfY3VydGlzX2RmKSA8LSBzYW1wbGVfaW5mb19sCiAgICBicmF5X2N1cnRpc19kZiRzYW1wbGVfaW5mbzEgPC0gcm93bmFtZXMoYnJheV9jdXJ0aXNfZGYpCiAgCiAgICBicmF5X2xvbmcgPC0gbWVsdChzZXREVChicmF5X2N1cnRpc19kZiksIAogICAgICAgICAgICAgICAgICAgICAgaWQudmFycyA9IGMoInNhbXBsZV9pbmZvMSIpLCAKICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAic2FtcGxlX2luZm8yIikgJT4lCiAgICAgIGZpbHRlcihzYW1wbGVfaW5mbzEgIT0gc2FtcGxlX2luZm8yKQogICAgYmlnX2JyYXlfbG9uZyA8LSByYmluZChiaWdfYnJheV9sb25nLCBicmF5X2xvbmcpCiAgfQp9CgpiaWdfYnJheV9sb25nIDwtIGJpZ19icmF5X2xvbmdbLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoInNhbXBsZUlEMSIsICJtYXNrX2lkMSIsICJkYXlfb2ZfbGlmZTEiKSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDo9IHRzdHJzcGxpdChzYW1wbGVfaW5mbzEsICJAIiwgZml4ZWQ9VFJVRSldCgpiaWdfYnJheV9sb25nIDwtIGJpZ19icmF5X2xvbmdbLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoInNhbXBsZUlEMiIsICJtYXNrX2lkMiIsICJkYXlfb2ZfbGlmZTIiKSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDo9IHRzdHJzcGxpdChzYW1wbGVfaW5mbzIsICJAIiwgZml4ZWQ9VFJVRSldICU+JQogIHNlbGVjdChjKHNhbXBsZUlEMSwgc2FtcGxlSUQyLCBtYXNrX2lkMSwgZGF5X29mX2xpZmUxLCBkYXlfb2ZfbGlmZTIsIHZhbHVlKSkgJT4lCiAgbXV0YXRlKGRheXNfYXBhcnQgPSBhcy5udW1lcmljKGRheV9vZl9saWZlMikgLSBhcy5udW1lcmljKGRheV9vZl9saWZlMSkpICU+JQogIGZpbHRlcihkYXlzX2FwYXJ0ID49IDEpIAogIApiaWdfYnJheV9sb25nIDwtIGJpZ19icmF5X2xvbmcgJT4lCiAgcmVuYW1lKGJyYXlfY3VydGlzID0gdmFsdWUpCmBgYAoKcXVpY2sgcGxvdCBvZiBkYXlzIGFwYXJ0IHZzIGJyYXktY3VydGlzIGRpc3RhbmNlLCB2aXJvbWUKCgoKCmBgYHtyfQptZXJnZShiaWdfYnJheV9sb25nLCBtZXRhX2R0LCAKICAgICAgYnkueCA9ICJzYW1wbGVJRDEiLCBieS55ID0gInNhbXBsZSIpICU+JQogIGdncGxvdChhZXMoeD0gZGF5c19hcGFydCwgeSA9IGJyYXlfY3VydGlzLCBjb2xvciA9IElBKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjAwNSkgKwogIGdlb21fc21vb3RoKCkgKwogIHRoZW1lX2J3KCkKICAKICAKYGBgCgoKZ2V0IGJyYXktY3VydGlzIGRpc3RhbmNlIHRvIG5leHQgc2FtcGxlIFZJUk9NRQoKYGBge3J9CnZpcl9uZXh0X2JyYXlfZHQgPC0gYmlnX2JyYXlfbG9uZyAlPiUKICBncm91cF9ieShtYXNrX2lkMSkgJT4lCiAgZmlsdGVyKG5fZGlzdGluY3Qoc2FtcGxlSUQxKSA+PSAyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlSUQxKSAlPiUKICBmaWx0ZXIoZGF5c19hcGFydCA9PSBtaW4oZGF5c19hcGFydCkpCgp2aXJfbmV4dF9icmF5X2R0IDwtIG1lcmdlKHZpcl9uZXh0X2JyYXlfZHQsIG1ldGFfZHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5LnggPSAic2FtcGxlSUQxIiwgYnkueSA9ICJzYW1wbGUiKQoKIyMgYWRkIGdyb3Vwcwp2aXJfbmV4dF9icmF5X2R0IDwtIG1lcmdlKHZpcl9uZXh0X2JyYXlfZHQsIGdyb3Vwc19kdCwgYnkgPSAibWFza19pZCIpCgpgYGAKCmBgYHtyfQoKSUFfbG0gPC0gbG0oZGF5c19hcGFydCB+IGJyYXlfY3VydGlzKklBLCAKICAgICAgICAgICAgIGRhdGE9IHZpcl9uZXh0X2JyYXlfZHQgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihkYXlzX2FwYXJ0IDw9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgIGRheXNfYXBhcnQgPj0gMTApKQoKKCBmaXR0ZWQuZW10IDwtIGVtdHJlbmRzKElBX2xtLCAiSUEiLCB2YXIgPSAiYnJheV9jdXJ0aXMiKSApCgpwd19JQSA8LSBwYWlycyhmaXR0ZWQuZW10KQoKcHdfSUEKYGBgCgoKYGBge3J9CnZpcl9kYXlzX3AgPC0gdmlyX25leHRfYnJheV9kdCAlPiUKICBmaWx0ZXIoZGF5c19hcGFydCA8PSAxMDAsCiAgICAgICAgIGRheXNfYXBhcnQgPj0xMCkgJT4lCiAgZ2dwbG90KGFlcyh4PSBkYXlzX2FwYXJ0LCB5ID0gYnJheV9jdXJ0aXMsIGNvbG9yID0gSUEpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMSkgKwogIGdlb21fc21vb3RoKAogICAgbWV0aG9kID0gImxtIgogICAgKSArCiAgZ2VvbV90ZXh0KHkgPSAwLjksCiAgICAgICAgICAgIHggPSA1MCwKICAgICAgICAgICAgbGFiZWwgPSAicC12YWx1ZSA9IDAuMDEwNyIsCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjNzk4RTg3IiwgIiNDMjdEMzgiKSkgKwogIHRoZW1lX2J3KCkgICsKICBsYWJzKHggPSAiRGF5cyB0byBOZXh0IFNhbXBsZSIsCiAgICAgICB5ID0gIkJyYXktQ3VydGlzIERpc3NpbWlsYXJpdHkiKQoKdmlyX2RheXNfcAoKZ2dzYXZlKAogIHZpcl9kYXlzX3AsCiAgZmlsZSA9IHNwcmludGYoCiAgICAiJXMvY2hhcnRzL2JyYXlfY3VydGlzX25leHRfdmlyX2RheXNfSUEucGRmIiwKICAgIGZpbmRfcnN0dWRpb19yb290X2ZpbGUoKQogICksCiAgaGVpZ2h0ID0gNSwgd2lkdGggPSAzLjUKKQpgYGAKCgphdmVyYWdlIGJyYXkgZGlzdGFuY2UgYnkgc3ViamVjdC9wYWlyIFZJUk9NRQpgYGB7cn0KdmlyX2JyYXlfc3VtX3N1YmogPC0gdmlyX25leHRfYnJheV9kdCAlPiUKICBncm91cF9ieShtYXNrX2lkMSkgJT4lCiAgc3VtbWFyaXplKGF2ZXJhZ2VfYnJheSA9IG1lYW4oYnJheV9jdXJ0aXMpLAogICAgICAgICAgICBJQSA9IElBLAogICAgICAgICAgICBwYWlyX251bSA9IGNhc2VfaW5kKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZGlzdGluY3QoKSAKCgp2aXJfYnJheV9zdW1fc3ViaiAlPiUKICBncm91cF9ieShwYWlyX251bSkgJT4lCiAgZmlsdGVyKG4oKSAhPSAyKSAlPiUKICBhcnJhbmdlKHBhaXJfbnVtKQpgYGAKCmBgYHtyfQoKCnZpcl9icmF5X3N1Ympfd2lkZSA8LSB2aXJfYnJheV9zdW1fc3ViaiAlPiUKICBncm91cF9ieShwYWlyX251bSkgJT4lCiAgIyBtYWtpbmcgc3VyZSB0aGVyZSBhcmUgMiBpbiBlYWNoIHBhaXIKICBmaWx0ZXIobigpID09IDIpICU+JQogIHVuZ3JvdXAoKSAlPiUKICAjIG1ha2luZyBzdXJlIHRoYXQgb25lIGlzIHllcyBhbmQgb25lIGlzIG5vIChmb3IgSUEpCiAgZ3JvdXBfYnkocGFpcl9udW0sIElBKSAlPiUKICBmaWx0ZXIobigpID09IDEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gcGFpcl9udW0sIG5hbWVzX2Zyb20gPSBJQSwgdmFsdWVzX2Zyb20gPSBhdmVyYWdlX2JyYXkpCmBgYAoKCmBgYHtyfQp2aXJfYnJheV9zdWJqX3dpZGUgJT4lCiAgZ2dwYWlyZWQoY29uZDEgPSAiWWVzIiwgY29uZDIgPSAiTm8iLAogICAgZmlsbCA9ICJjb25kaXRpb24iLCBwYWxldHRlID0gYygiIzc5OEU4NyIsICIjQzI3RDM4IiksCiAgICBsaW5lLmNvbG9yID0gImxpZ2h0Z3JleSIsIGdndGhlbWUgPSB0aGVtZV9idygpLCBsaW5lLnNpemUgPSAwLjEpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMocGFpcmVkID0gVFJVRSkgCiAgCmBgYAoKcGFpcmVkIHdpbGNveCB0ZXN0CmBgYHtyfQp3aWxjb3gudGVzdCh2aXJfYnJheV9zdWJqX3dpZGUkWWVzLCB2aXJfYnJheV9zdWJqX3dpZGUkTm8sIHBhaXJlZCA9IFRSVUUpCgpgYGAKCgpuZXcgcGxvdCBhdmVyYWdlIGJyYXkgZGlzdGFuY2UgYnkgVDFEIGFuZCBjb3VudHJ5IHN0YXR1cyBWSVJPTUUKYGBge3J9Cgp2aXJfYnJheV9zdW1fc3ViaiAgJT4lCiAgZ3JvdXBfYnkocGFpcl9udW0pICU+JQogIGZpbHRlcihuKCkgPT0gMikgJT4lCiAgZ2dwbG90KGFlcyhJQSwgYXZlcmFnZV9icmF5KSkgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IElBKSwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoY29sb3I9ImdyZXkyMCIsIGFscGhhID0gMC41LCBzdHJva2UgPSAwKSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHBhaXJfbnVtKSwgY29sb3IgPSAibGlnaHRncmV5IiwgYWxwaGEgPSAwLjMpICsKICBnZW9tX3RleHQobGFiZWwgPSAiV2lsY294b24sXG5wLXZhbHVlID0gMC4yNzM0IiwgeCA9ICJObyIsIAogICAgICAgICAgICB5ID0gMSwgc2l6ZSA9IDgvLnB0KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM3OThFODciLCAiI0MyN0QzOCIpKSArCiAgeWxpbShjKDAuMiwgMS4wNSkpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHkgPSAiQnJheS1DdXJ0aXMgRGlzdGFuY2VcbnRvIEZvbGxvdy11cCBTYW1wbGUiLAogICAgICAgdGl0bGU9InZpcnVzIFNHQnMiKQoKZ2dzYXZlKGZpbGUgPSBzcHJpbnRmKCIlcy9jaGFydHMvdmlyb21lX2JyYXlfY3VydGlzX0lBX05DQ19wYWlyZWQucGRmIiwKICAgICAgICAgICAgICAgICAgICAgIGZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSksIAogICAgICAgICAgICAgICAgIHdpZHRoID0gMi41LCBoZWlnaHQgPSAzLjUpICAKYGBgCgoKYGBge3J9CnZpcl9icmF5X3N1bV9zdWJqICAlPiUKICBncm91cF9ieShwYWlyX251bSkgJT4lCiAgZmlsdGVyKG4oKSA9PSAyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoSUEpICU+JQogIHN1bW1hcml6ZShhdmdfYnJheV90MWQgPSBtZWFuKGF2ZXJhZ2VfYnJheSksCiAgICAgICAgICAgIG4gPSBuX2Rpc3RpbmN0KG1hc2tfaWQxKSkKYGBgCgoKYGBge3J9CiMjIHRoaXMgZnVuY3Rpb25zIHJvdW5kcyBhIG51bWJlciB1cCB0byB0aGUgbmVhcmVzdCAxMDAKcm91bmRfYW55ID0gZnVuY3Rpb24oeCwgYWNjdXJhY3ksIGY9Y2VpbGluZyl7Zih4LyBhY2N1cmFjeSkgKiBhY2N1cmFjeX0KCkRPTF92aXJfcGFpcl9kdCA8LSB2aXJfbmV4dF9icmF5X2R0ICU+JQogIG11dGF0ZShyb3VuZGVkX0RPTCA9ICByb3VuZF9hbnkoYWdlX2RheXMsIDEwMCkpICU+JQogIGZpbHRlcihyb3VuZGVkX0RPTCA8PSAxNDAwKSAlPiUKICBncm91cF9ieShtYXNrX2lkMSwgcm91bmRlZF9ET0wpICU+JQogIHN1bW1hcml6ZShhdmVyYWdlX2JyYXkgPSBtZWFuKGJyYXlfY3VydGlzKSwKICAgICAgICAgICAgSUEgPSBJQSwKICAgICAgICAgICAgcGFpcl9udW0gPSBjYXNlX2luZCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRpc3RpbmN0KCkgCmBgYAoKCgpgYGB7cn0KRE9MX3Zpcl9wYWlyX3N1Ympfd2lkZSA8LSBET0xfdmlyX3BhaXJfZHQgJT4lCiAgZ3JvdXBfYnkocGFpcl9udW0sIHJvdW5kZWRfRE9MKSAlPiUKICAjIG1ha2luZyBzdXJlIHRoZXJlIGFyZSAyIGluIGVhY2ggcGFpcgogIGZpbHRlcihuKCkgPT0gMikgJT4lCiAgdW5ncm91cCgpICU+JQogICMgbWFraW5nIHN1cmUgdGhhdCBvbmUgaXMgeWVzIGFuZCBvbmUgaXMgbm8gKGZvciBJQSkKICBncm91cF9ieShwYWlyX251bSwgSUEsIHJvdW5kZWRfRE9MKSAlPiUKICBmaWx0ZXIobigpID09IDEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gYyhwYWlyX251bSwgcm91bmRlZF9ET0wpLCBuYW1lc19mcm9tID0gSUEsIAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYXZlcmFnZV9icmF5KQpgYGAKCmBgYHtyfQpET0xfdmlyX3BhaXJfc3Vial93aWRlICU+JQogIGdncGFpcmVkKGNvbmQxID0gIlllcyIsIGNvbmQyID0gIk5vIiwKICAgIGZpbGwgPSAiY29uZGl0aW9uIiwgCiAgICBwYWxldHRlID0gYygiIzc5OEU4NyIsICIjQzI3RDM4IiksCiAgICBsaW5lLmNvbG9yID0gImxpZ2h0Z3JleSIsIGdndGhlbWUgPSB0aGVtZV9idygpLCBsaW5lLnNpemUgPSAwLjEsCiAgICBmYWNldC5ieSA9ICJyb3VuZGVkX0RPTCIpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMocGFpcmVkID0gVFJVRSkgCmBgYAoKYGBge3J9CkRPTF92aXJfcGFpcl93aWxjb3hfZHQgPC0gRE9MX3Zpcl9wYWlyX3N1Ympfd2lkZSAlPiUgCiAgZ3JvdXBfYnkocm91bmRlZF9ET0wpICU+JQogIGRvKHcgPSB3aWxjb3gudGVzdCguJFllcywgLiRObywgZGF0YT0uLCBwYWlyZWQ9VFJVRSkpICU+JSAKICAgICAgIHN1bW1hcmlzZShyb3VuZGVkX0RPTCwgCiAgICAgICAgICAgICAgICAgV2lsY294ID0gdyRwLnZhbHVlLAogICAgICAgICAgICAgICAgIGFzdGVyID0gY2FzZV93aGVuKFdpbGNveCA8IDAuMDAwMSB+ICIqKioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXaWxjb3ggPCAwLjAwMSB+ICIqKioiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXaWxjb3ggPCAwLjAxIH4gIioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXaWxjb3ggPCAwLjA1IH4gIioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAibnMiKSkKCkRPTF92aXJfcGFpcl93aWxfZHQgPC0gbWVyZ2UoRE9MX3Zpcl9wYWlyX2R0LCBET0xfdmlyX3BhaXJfd2lsY294X2R0LCBieSA9ICJyb3VuZGVkX0RPTCIpCmBgYAoKYGBge3J9CkRPTF92aXJfcGFpcl93aWxfZHQgJT4lCiMgIGdyb3VwX2J5KHBhaXJfbnVtKSAlPiUKICAjIG1ha2luZyBzdXJlIHRoZXJlIGFyZSAyIGluIGVhY2ggcGFpcgojICBmaWx0ZXIobigpID09IDIpICU+JQojICB1bmdyb3VwKCkgJT4lCiAgIyBtYWtpbmcgc3VyZSB0aGF0IG9uZSBpcyB5ZXMgYW5kIG9uZSBpcyBubyAoZm9yIElBKQojICBncm91cF9ieShwYWlyX251bSwgSUEpICU+JQojICBmaWx0ZXIobigpID09IDEpICU+JQojICB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyhJQSwgYXZlcmFnZV9icmF5KSkgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IElBKSwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoY29sb3I9ImdyZXkyMCIsIGFscGhhID0gMC41LCBzdHJva2UgPSAwLCBzaXplID0gMC44KSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHBhaXJfbnVtKSwgY29sb3IgPSAibGlnaHRncmV5IiwgYWxwaGEgPSAwLjMpICsKICBnZW9tX3RleHQoZGF0YSA9IERPTF92aXJfcGFpcl93aWxjb3hfZHQsIGFlcyhsYWJlbCA9IGFzdGVyKSwgeCA9ICJObyIsIHkgPSAxLjA1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM3OThFODciLCAiI0MyN0QzOCIpKSArCiAgeWxpbShjKE5BLCAxLjEpKSArCiAgZmFjZXRfd3JhcCh2YXJzKHJvdW5kZWRfRE9MKSwgbnJvdyA9IDEpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSAiZGF5IG9mIGxpZmUgKHJvdW5kZWQpIiwgCiAgICAgICB5ID0gIkJyYXktQ3VydGlzIERpc3RhbmNlXG50byBGb2xsb3ctdXAgU2FtcGxlIiwKICAgICAgIHRpdGxlPSJ2aXJ1cyBTR0JzIikKCmdnc2F2ZShmaWxlID0gc3ByaW50ZigiJXMvY2hhcnRzL3Zpcm9tZV9JQV9hbmRfZGF5b2ZsaWZlX2JyYXlfY3VydGlzX05DQ19wYWlyZWQucGRmIiwKICAgICAgICAgICAgICAgICAgICAgIGZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSksIAogICAgICAgICAgICAgICAgIHdpZHRoID0gNiwgaGVpZ2h0ID0gMy41KSAgCmBgYAoKCgppbnRyYXN1YmplY3QgYnJheS1jdXJ0aXMsIEJBQ1RFUklPTUUKCmBgYHtyfQoKbWFza19pZF9saXN0IDwtIGFzLmxpc3QoCiAgbWVyZ2VfZHQgJT4lIGRpc3RpbmN0KG1hc2tfaWQpCikKCmJhY19icmF5X2xvbmcgPC0gZGF0YS50YWJsZShzYW1wbGVfaW5mbzE9Y2hhcmFjdGVyKCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX2luZm8yPWNoYXJhY3RlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPW51bWVyaWMoKSkKCmZvciAoc3ViamVjdHEgaW4gbWFza19pZF9saXN0WzFdJG1hc2tfaWQpIHsKICB0ZW1wX2xvbmdfZHQgPC0gbWVyZ2VfZHQgJT4lCiAgICBmaWx0ZXIobWFza19pZCA9PSBzdWJqZWN0cSkKICAKICB0ZW1wX2xvbmdfZHQkc2FtcGxlX2luZm8gPC0gc3RyX2ModGVtcF9sb25nX2R0JHNhbXBsZUlELCAiQCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZW1wX2xvbmdfZHQkbWFza19pZCwgIkAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVtcF9sb25nX2R0JGFnZV9kYXlzKQogIAogIHRlbXBfbG9uZ19kdCA8LSB0ZW1wX2xvbmdfZHQgJT4lCiAgICBzZWxlY3QoYyhzYW1wbGVfaW5mbywgcmVsX2FidW5kYW5jZSwgbGluZWFnZSwga2luZ2RvbSkpICU+JQogICAgZGlzdGluY3QoKQogIGlmKG5yb3codGVtcF9sb25nX2R0KSA+IDEpewogICAgd2lkZV92aXJvbWVfdGVtcF9kdCA8LSB0ZW1wX2xvbmdfZHQgJT4lCiAgICAgIGZpbHRlcihraW5nZG9tID09ICJCYWN0ZXJpYSIpICU+JQogICAgICBzZWxlY3QoLWtpbmdkb20pICU+JQogICAgICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBsaW5lYWdlLCAKICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSByZWxfYWJ1bmRhbmNlLCAKICAgICAgICAgICAgICAgICAgdmFsdWVzX2ZpbGwgPSAwKQogICAgc2FtcGxlX2luZm9fbCA8LSB3aWRlX3Zpcm9tZV90ZW1wX2R0JHNhbXBsZV9pbmZvCiAgCiAgICB3aWRlX3Zpcm9tZV90ZW1wX2R0IDwtIHdpZGVfdmlyb21lX3RlbXBfZHQgJT4lIHNlbGVjdCgtc2FtcGxlX2luZm8pCiAgICAKICAgIGJyYXlfY3VydGlzX2Rpc3QgPC0gdmVnZGlzdCh3aWRlX3Zpcm9tZV90ZW1wX2R0LCBtZXRob2Q9ImJyYXkiKQogICAgCiAgICBicmF5X2N1cnRpc19tYXQgPC0gYXMubWF0cml4KGJyYXlfY3VydGlzX2Rpc3QpCiAgICAKICAgIGJyYXlfY3VydGlzX2RmIDwtIGFzLmRhdGEuZnJhbWUoYnJheV9jdXJ0aXNfbWF0KQogICAgCiAgICBjb2xuYW1lcyhicmF5X2N1cnRpc19kZikgPC0gc2FtcGxlX2luZm9fbAogICAgcm93bmFtZXMoYnJheV9jdXJ0aXNfZGYpIDwtIHNhbXBsZV9pbmZvX2wKICAgIGJyYXlfY3VydGlzX2RmJHNhbXBsZV9pbmZvMSA8LSByb3duYW1lcyhicmF5X2N1cnRpc19kZikKICAKICAgIGJyYXlfbG9uZyA8LSBtZWx0KHNldERUKGJyYXlfY3VydGlzX2RmKSwgCiAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gYygic2FtcGxlX2luZm8xIiksIAogICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJzYW1wbGVfaW5mbzIiKSAlPiUKICAgICAgZmlsdGVyKHNhbXBsZV9pbmZvMSAhPSBzYW1wbGVfaW5mbzIpCiAgICBiYWNfYnJheV9sb25nIDwtIHJiaW5kKGJhY19icmF5X2xvbmcsIGJyYXlfbG9uZykKICB9Cn0KCmJhY19icmF5X2xvbmcgPC0gYmFjX2JyYXlfbG9uZ1ssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygic2FtcGxlSUQxIiwgIm1hc2tfaWQxIiwgImRheV9vZl9saWZlMSIpIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOj0gdHN0cnNwbGl0KHNhbXBsZV9pbmZvMSwgIkAiLCBmaXhlZD1UUlVFKV0KCmJhY19icmF5X2xvbmcgPC0gYmFjX2JyYXlfbG9uZ1ssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygic2FtcGxlSUQyIiwgIm1hc2tfaWQyIiwgImRheV9vZl9saWZlMiIpIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOj0gdHN0cnNwbGl0KHNhbXBsZV9pbmZvMiwgIkAiLCBmaXhlZD1UUlVFKV0gJT4lCiAgc2VsZWN0KGMoc2FtcGxlSUQxLCBzYW1wbGVJRDIsIG1hc2tfaWQxLCBkYXlfb2ZfbGlmZTEsIGRheV9vZl9saWZlMiwgdmFsdWUpKSAlPiUKICBtdXRhdGUoZGF5c19hcGFydCA9IGFzLm51bWVyaWMoZGF5X29mX2xpZmUyKSAtIGFzLm51bWVyaWMoZGF5X29mX2xpZmUxKSkgJT4lCiAgZmlsdGVyKGRheXNfYXBhcnQgPj0gMSkgCiAgCmJhY19icmF5X2xvbmcgPC0gYmFjX2JyYXlfbG9uZyAlPiUKICByZW5hbWUoYnJheV9jdXJ0aXMgPSB2YWx1ZSkKYGBgCgpxdWljayBwbG90IG9mIGRheXMgYXBhcnQgdnMgYnJheS1jdXJ0aXMgZGlzdGFuY2UsIGJhY3RlcmlvbWUKCmBgYHtyfQpiYWNfYnJheV9sb25nICU+JQogIGdncGxvdChhZXMoeD0gZGF5c19hcGFydCwgeSA9IGJyYXlfY3VydGlzKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjAwNSwgY29sb3IgPSAiY2FkZXRibHVlIikgKwogIGdlb21fc21vb3RoKCkgKwogIHRoZW1lX2J3KCkKYGBgCgpnZXQgYnJheS1jdXJ0aXMgZGlzdGFuY2UgdG8gbmV4dCBzYW1wbGUgQkFDVEVSSU9NRQoKYGBge3J9CmJhY19uZXh0X2JyYXlfZHQgPC0gYmFjX2JyYXlfbG9uZyAlPiUKICBncm91cF9ieShtYXNrX2lkMSkgJT4lCiAgZmlsdGVyKG5fZGlzdGluY3Qoc2FtcGxlSUQxKSA+PSAyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoc2FtcGxlSUQxKSAlPiUKICBmaWx0ZXIoZGF5c19hcGFydCA9PSBtaW4oZGF5c19hcGFydCkpCgpiYWNfbmV4dF9icmF5X2R0IDwtIG1lcmdlKGJhY19uZXh0X2JyYXlfZHQsIG1ldGFfZHQsIGJ5LnggPSAic2FtcGxlSUQxIiwgYnkueSA9ICJzYW1wbGUiKQoKIyMgYWRkIGdyb3VwcwpiYWNfbmV4dF9icmF5X2R0IDwtIG1lcmdlKGJhY19uZXh0X2JyYXlfZHQsIGdyb3Vwc19kdCwgYnkgPSAibWFza19pZCIpCgpgYGAKCgoKYGBge3J9CgpiYWNfSUFfbG0gPC0gbG0oZGF5c19hcGFydCB+IGJyYXlfY3VydGlzKklBLCAKICAgICAgICAgICAgIGRhdGE9IGJhY19uZXh0X2JyYXlfZHQgJT4lCiAgICAgICAgICAgICAgIGZpbHRlcihkYXlzX2FwYXJ0IDw9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgIGRheXNfYXBhcnQgPj0gMTApKQoKKCBiYWNfZml0dGVkLmVtdCA8LSBlbXRyZW5kcyhiYWNfSUFfbG0sICJJQSIsIHZhciA9ICJicmF5X2N1cnRpcyIpICkKCnB3X2JhY19JQSA8LSBwYWlycyhiYWNfZml0dGVkLmVtdCkKCnB3X2JhY19JQQpgYGAKCgpgYGB7cn0KYmFjX2RheXNfcCA8LSBiYWNfbmV4dF9icmF5X2R0ICU+JQogIGZpbHRlcihkYXlzX2FwYXJ0IDw9IDEwMCwKICAgICAgICAgZGF5c19hcGFydCA+PTEwKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IGRheXNfYXBhcnQsIHkgPSBicmF5X2N1cnRpcywgY29sb3IgPSBJQSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4xKSArCiAgZ2VvbV9zbW9vdGgoCiAgICBtZXRob2QgPSAibG0iCiAgICApICsKICBnZW9tX3RleHQoeSA9IDAuOSwgeCA9IDUwLAogICAgICAgICAgICBsYWJlbCA9ICJwLXZhbHVlID0gMC4zMTY5IiwKICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiM3OThFODciLCAiI0MyN0QzOCIpKSArCiAgdGhlbWVfYncoKSArCiAgbGFicyh4ID0gIkRheXMgdG8gTmV4dCBTYW1wbGUiLAogICAgICAgeSA9ICJCcmF5LUN1cnRpcyBEaXNzaW1pbGFyaXR5IikKCmJhY19kYXlzX3AKCmdnc2F2ZSgKICBiYWNfZGF5c19wLAogIGZpbGUgPSBzcHJpbnRmKAogICAgIiVzL2NoYXJ0cy9icmF5X2N1cnRpc19JQV9uZXh0X2JhY19kYXlzMS5wZGYiLAogICAgZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpCiAgKSwKICBoZWlnaHQgPSA1LCB3aWR0aCA9IDMuNQopCmBgYAoKCgphdmVyYWdlIGJyYXkgZGlzdGFuY2UgYnkgc3ViamVjdC9wYWlyIEJBQ1RFUklPTUUKYGBge3J9CmJhY19icmF5X3N1bV9zdWJqIDwtIGJhY19uZXh0X2JyYXlfZHQgJT4lCiAgZ3JvdXBfYnkobWFza19pZDEpICU+JQogIHN1bW1hcml6ZShhdmVyYWdlX2JyYXkgPSBtZWFuKGJyYXlfY3VydGlzKSwKICAgICAgICAgICAgSUEgPSBJQSwKICAgICAgICAgICAgcGFpcl9udW0gPSBjYXNlX2luZCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRpc3RpbmN0KCkgCmBgYAoKYGBge3J9CgoKYmFjX2JyYXlfc3Vial93aWRlIDwtIGJhY19icmF5X3N1bV9zdWJqICU+JQogIGdyb3VwX2J5KHBhaXJfbnVtKSAlPiUKICBmaWx0ZXIobigpID09IDIpICU+JQogIHVuZ3JvdXAoKSAlPiUKICAjIG1ha2luZyBzdXJlIHRoYXQgb25lIGlzIHllcyBhbmQgb25lIGlzIG5vIChmb3IgSUEpCiAgZ3JvdXBfYnkocGFpcl9udW0sIElBKSAlPiUKICBmaWx0ZXIobigpID09IDEpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBwaXZvdF93aWRlcihpZF9jb2xzID0gcGFpcl9udW0sIG5hbWVzX2Zyb20gPSBJQSwgdmFsdWVzX2Zyb20gPSBhdmVyYWdlX2JyYXkpCmBgYAoKYGBge3J9CmJhY19icmF5X3N1Ympfd2lkZSAlPiUKICBnZ3BhaXJlZChjb25kMSA9ICJZZXMiLCBjb25kMiA9ICJObyIsCiAgICBmaWxsID0gImNvbmRpdGlvbiIsIHBhbGV0dGUgPSBjKCIjNzk4RTg3IiwgIiNDMjdEMzgiKSwKICAgIGxpbmUuY29sb3IgPSAibGlnaHRncmV5IiwgZ2d0aGVtZSA9IHRoZW1lX2J3KCksIGxpbmUuc2l6ZSA9IDAuMSkgKwogIHN0YXRfY29tcGFyZV9tZWFucyhwYWlyZWQgPSBUUlVFKSAKICAKYGBgCgpgYGB7cn0Kd2lsY294LnRlc3QoYmFjX2JyYXlfc3Vial93aWRlJFllcywgYmFjX2JyYXlfc3Vial93aWRlJE5vLCBwYWlyZWQgPSBUUlVFKQoKYGBgCgpuZXcgcGxvdCBhdmVyYWdlIGJyYXkgZGlzdGFuY2UgYnkgSUEgYW5kIGNvdW50cnkgc3RhdHVzIGJhY3Rlcmlvb21lCmBgYHtyfQoKYmFjX2JyYXlfc3VtX3N1YmogJT4lCiAgZ3JvdXBfYnkocGFpcl9udW0pICU+JQogIGZpbHRlcihuKCkgPT0gMikgJT4lCiAgdW5ncm91cCgpICU+JQogICMgbWFraW5nIHN1cmUgdGhhdCBvbmUgaXMgeWVzIGFuZCBvbmUgaXMgbm8gKGZvciBJQSkKICBncm91cF9ieShwYWlyX251bSwgSUEpICU+JQogIGZpbHRlcihuKCkgPT0gMSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMoSUEsIGF2ZXJhZ2VfYnJheSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGZpbGwgPSBJQSksIG91dGxpZXIuc2hhcGUgPSBOQSwgYWxwaGEgPSAwLjYpICsKICBnZW9tX3BvaW50KGNvbG9yPSJncmV5MjAiLCBhbHBoYSA9IDAuNSwgc3Ryb2tlID0gMCkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBwYWlyX251bSksIGNvbG9yID0gImxpZ2h0Z3JleSIsIGFscGhhID0gMC4yKSArCiAgZ2VvbV90ZXh0KGxhYmVsID0gIldpbGNveG9uLFxucC12YWx1ZSA9IDAuMjY4MyIsIHggPSAiTm8iLCAKICAgICAgICAgICAgeSA9IDEsIHNpemUgPSA4Ly5wdCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNzk4RTg3IiwgIiNDMjdEMzgiKSkgKwogIHlsaW0oYygwLjIsIDEuMDUpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh5ID0gIkJyYXktQ3VydGlzIERpc3RhbmNlXG50byBGb2xsb3ctdXAgU2FtcGxlIiwKICAgICAgIHRpdGxlPSJiYWN0ZXJpYSBTR0JzIikKCmdnc2F2ZShmaWxlID0gc3ByaW50ZigiJXMvY2hhcnRzL2JhY3RlcmlvbWVfYnJheV9jdXJ0aXNfSUFfTkNDX3BhaXJlZC5wZGYiLAogICAgICAgICAgICAgICAgICAgICAgZmluZF9yc3R1ZGlvX3Jvb3RfZmlsZSgpKSwgCiAgICAgICAgICAgICAgICAgd2lkdGggPSAyLjUsIGhlaWdodCA9IDMuNSkgIApgYGAKCgoKRE9MIEJBQ1RFUklPTUUKYGBge3J9CiMjIHRoaXMgZnVuY3Rpb25zIHJvdW5kcyBhIG51bWJlciB1cCB0byB0aGUgbmVhcmVzdCAxMDAKcm91bmRfYW55ID0gZnVuY3Rpb24oeCwgYWNjdXJhY3ksIGY9Y2VpbGluZyl7Zih4LyBhY2N1cmFjeSkgKiBhY2N1cmFjeX0KCkRPTF9iYWNfcGFpcl9kdCA8LSBiYWNfbmV4dF9icmF5X2R0ICU+JQogIG11dGF0ZShyb3VuZGVkX0RPTCA9ICByb3VuZF9hbnkoYWdlX2RheXMsIDEwMCkpICU+JQogIGZpbHRlcihyb3VuZGVkX0RPTCA8PSAxNDAwKSAlPiUKICBncm91cF9ieShtYXNrX2lkMSwgcm91bmRlZF9ET0wpICU+JQogIHN1bW1hcml6ZShhdmVyYWdlX2JyYXkgPSBtZWFuKGJyYXlfY3VydGlzKSwKICAgICAgICAgICAgSUEgPSBJQSwKICAgICAgICAgICAgcGFpcl9udW0gPSBjYXNlX2luZCkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRpc3RpbmN0KCkgCmBgYAoKYGBge3J9CkRPTF9iYWNfcGFpcl9zdWJqX3dpZGUgPC0gRE9MX2JhY19wYWlyX2R0ICU+JQogIGdyb3VwX2J5KHBhaXJfbnVtLCByb3VuZGVkX0RPTCkgJT4lCiAgZmlsdGVyKG4oKSA9PSAyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkocGFpcl9udW0sIHJvdW5kZWRfRE9MLCBJQSkgJT4lCiAgZmlsdGVyKG4oKSA9PSAxKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGMocGFpcl9udW0sIHJvdW5kZWRfRE9MKSwgbmFtZXNfZnJvbSA9IElBLCB2YWx1ZXNfZnJvbSA9IGF2ZXJhZ2VfYnJheSkKYGBgCgpgYGB7cn0KRE9MX2JhY19wYWlyX3N1Ympfd2lkZSAlPiUKICBnZ3BhaXJlZChjb25kMSA9ICJZZXMiLCBjb25kMiA9ICJObyIsCiAgICBmaWxsID0gImNvbmRpdGlvbiIsIHBhbGV0dGUgPSBjKCIjNzk4RTg3IiwgIiNDMjdEMzgiKSwKICAgIGxpbmUuY29sb3IgPSAibGlnaHRncmV5IiwgZ2d0aGVtZSA9IHRoZW1lX2J3KCksIGxpbmUuc2l6ZSA9IDAuMSwKICAgIGZhY2V0LmJ5ID0gInJvdW5kZWRfRE9MIikgKwogIHN0YXRfY29tcGFyZV9tZWFucyhwYWlyZWQgPSBUUlVFKSAKYGBgCgpgYGB7cn0KRE9MX2JhY19wYWlyX3dpbGNveF9kdCA8LSBET0xfYmFjX3BhaXJfc3Vial93aWRlICU+JSAKICBncm91cF9ieShyb3VuZGVkX0RPTCkgJT4lCiAgZG8odyA9IHdpbGNveC50ZXN0KC4kWWVzLCAuJE5vLCBkYXRhPS4sIHBhaXJlZD1UUlVFKSkgJT4lIAogICAgICAgc3VtbWFyaXNlKHJvdW5kZWRfRE9MLCAKICAgICAgICAgICAgICAgICBXaWxjb3ggPSB3JHAudmFsdWUsCiAgICAgICAgICAgICAgICAgYXN0ZXIgPSBjYXNlX3doZW4oV2lsY294IDwgMC4wMDAxIH4gIioqKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdpbGNveCA8IDAuMDAxIH4gIioqKiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdpbGNveCA8IDAuMDEgfiAiKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdpbGNveCA8IDAuMDUgfiAiKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJucyIpKQoKRE9MX2JhY19wYWlyX3dpbF9kdCA8LSBtZXJnZShET0xfYmFjX3BhaXJfZHQsIERPTF9iYWNfcGFpcl93aWxjb3hfZHQsIGJ5ID0gInJvdW5kZWRfRE9MIikKYGBgCgpgYGB7cn0KRE9MX2JhY19wYWlyX3dpbF9kdCAlPiUKICBncm91cF9ieShyb3VuZGVkX0RPTCxwYWlyX251bSkgJT4lCiAgZmlsdGVyKG4oKSA9PSAyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkocGFpcl9udW0sIHJvdW5kZWRfRE9MLCBJQSkgJT4lCiAgZmlsdGVyKG4oKSA9PSAxKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyhJQSwgYXZlcmFnZV9icmF5KSkgKwogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IElBKSwgb3V0bGllci5zaGFwZSA9IE5BLCBhbHBoYSA9IDAuNikgKwogIGdlb21fcG9pbnQoY29sb3I9ImdyZXkyMCIsIGFscGhhID0gMC41LCBzdHJva2UgPSAwLCBzaXplID0gMC44KSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IHBhaXJfbnVtKSwgY29sb3IgPSAibGlnaHRncmV5IiwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3RleHQoZGF0YSA9IERPTF9iYWNfcGFpcl93aWxjb3hfZHQsIGFlcyhsYWJlbCA9IGFzdGVyKSwgeCA9ICJObyIsIHkgPSAxLjA1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM3OThFODciLCAiI0MyN0QzOCIpKSArCiAgeWxpbShjKE5BLCAxLjEpKSArCiAgZmFjZXRfd3JhcCh2YXJzKHJvdW5kZWRfRE9MKSwgbnJvdyA9IDEpICsKICB0aGVtZV9idygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHggPSAiZGF5IG9mIGxpZmUgKHJvdW5kZWQpIiwgCiAgICAgICB5ID0gIkJyYXktQ3VydGlzIERpc3RhbmNlXG50byBGb2xsb3ctdXAgU2FtcGxlIiwKICAgICAgIHRpdGxlPSJiYWN0ZXJpYSBTR0JzIikKCmdnc2F2ZShmaWxlID0gc3ByaW50ZigiJXMvY2hhcnRzL2JhY3RlcmlvbWVfSUFfYW5kX2RheW9mbGlmZV9icmF5X2N1cnRpc19OQ0NfcGFpcmVkLnBkZiIsCiAgICAgICAgICAgICAgICAgICAgICBmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpLCAKICAgICAgICAgICAgICAgICB3aWR0aCA9IDYsIGhlaWdodCA9IDMuNSkgIApgYGAKCgoK